package com.example.tcp.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.example.common.ResponseVO;
import com.example.common.constant.ConsoleColors;
import com.example.common.constant.Constants;
import com.example.common.enums.ImConnectStatusEnum;
import com.example.common.enums.command.GroupEventCommand;
import com.example.common.enums.command.MessageCommand;
import com.example.common.enums.command.SystemCommand;
import com.example.common.enums.command.UserEventCommand;
import com.example.common.model.UserClientDto;
import com.example.common.model.UserSession;
import com.example.common.model.message.CheckSendMessageReq;
import com.example.pack.LoginPack;
import com.example.pack.message.ChatMessageAck;
import com.example.pack.user.UserStatusChangeNotifyPack;
import com.example.proto.Message;
import com.example.proto.MessagePack;
import com.example.tcp.feign.FeignMessageService;
import com.example.tcp.publish.MqMessageProducer;
import com.example.tcp.redis.RedisManager;
import com.example.tcp.utils.SessionSocketHolder;
import feign.Feign;
import feign.Request;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.cache.CacheProperties;

import java.net.InetAddress;

@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<Message> {

    private Integer brokerId;


    private FeignMessageService feignMessageService;


    public NettyServerHandler(Integer brokerId,String  logicUrl) {
        this.brokerId = brokerId;

        feignMessageService=Feign.builder()    //用于配置和创建 Feign 客户端接口的实例
                .encoder(new JacksonEncoder())   //设置了请求编码器
                .decoder(new JacksonDecoder())     //设置了响应解码器
                //设置超时时间
//                .options(new Request.Options(1000,3500))
                .target(FeignMessageService.class,logicUrl);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message message) throws Exception {

        Integer command=message.getMessageHeader().getCommand();
        //登入command
        if(command== SystemCommand.LOGIN.getCommand()){
            //用户的登入
            LoginPack loginPack= JSON.parseObject(JSONObject.toJSONString(message.getMessagePack()),new TypeReference<LoginPack>(){}.getType());
            //存入用户的id 到channel 中 用来区分
            ctx.channel().attr(AttributeKey.valueOf(Constants.UserId)).set(loginPack.getUserId());                    //用户的id
            ctx.channel().attr(AttributeKey.valueOf(Constants.AppId)).set(message.getMessageHeader().getAppId());       //应用的id
            ctx.channel().attr(AttributeKey.valueOf(Constants.ClientType)).set(message.getMessageHeader().getClientType());  //客户端类型
            ctx.channel().attr(AttributeKey.valueOf(Constants.Imei)).set(message.getMessageHeader().getImei());         //设备号
            //将channel存起来

            // 使用redis redis map   保存用户相关的登入信息  想userid 和appid
            UserSession userSession = new UserSession();
            userSession.setAppId(message.getMessageHeader().getAppId());
            userSession.setClientType(message.getMessageHeader().getClientType());
            userSession.setUserId(loginPack.getUserId());
            userSession.setConnectState(ImConnectStatusEnum.ONLINE_STATUS.getCode());    //表示为在线状态
            userSession.setBrokerId(brokerId);
            userSession.setImei(message.getMessageHeader().getImei());


            try{
                InetAddress localHost = InetAddress.getLocalHost();
                userSession.setBrokerHost(localHost.getHostAddress());
            }catch (Exception e){
                e.printStackTrace();
            }

            //redisson  保存到redis
            RedissonClient redissonClient = RedisManager.getRedissonClient();
            RMap<String, String> map = redissonClient.getMap(message.getMessageHeader().getAppId() + Constants.RedisConstants.UserSessionConstants + loginPack.getUserId());
            map.put(message.getMessageHeader().getClientType()+":"+message.getMessageHeader().getImei(),JSONObject.toJSONString(userSession));     //这里是用ClientType作为key值
            // 保存到 CHANENELS 里面  保存到session
            SessionSocketHolder.put(message.getMessageHeader().getAppId(),
                    loginPack.getUserId(),message.getMessageHeader().getClientType(),
                    message.getMessageHeader().getImei(),(NioSocketChannel) ctx.channel());

            //登入时 进行通知 其他段信息  userclientdto
            UserClientDto userClientDto=new UserClientDto();
            userClientDto.setImei(message.getMessageHeader().getImei());
            userClientDto.setUserId(loginPack.getUserId());
            userClientDto.setAppId(message.getMessageHeader().getAppId());
            userClientDto.setClientType(message.getMessageHeader().getClientType());
            //这里使用的时redis的广播模式  来进行通知
            RTopic topic = redissonClient.getTopic(Constants.RedisConstants.UserLoginChannel);
            topic.publish(JSONObject.toJSONString(userClientDto));     //对于收到消息需要进行踢人下线的处理

            //登入需将实现上线的
            UserStatusChangeNotifyPack userStatusChangeNotifyPack = new UserStatusChangeNotifyPack();
            userStatusChangeNotifyPack.setAppId(message.getMessageHeader().getAppId());
            userStatusChangeNotifyPack.setUserId(loginPack.getUserId());
            userStatusChangeNotifyPack.setStatus(ImConnectStatusEnum.ONLINE_STATUS.getCode());
            //发送给mq
            MqMessageProducer.sendMessage(userStatusChangeNotifyPack,message.getMessageHeader(),
                    UserEventCommand.USER_ONLINE_STATUS_CHANGE.getCommand());
            //告诉用户已经登入成功了
            MessagePack<LoginPack> loginSuccess = new MessagePack<>();
            LoginPack loginAckPack = new LoginPack();
            loginAckPack.setUserId(loginPack.getUserId());
            loginSuccess.setCommand(SystemCommand.LOGINACK.getCommand());     //设置登入成功
            loginSuccess.setData(loginAckPack);
            loginSuccess.setImei(message.getMessageHeader().getImei());
            loginSuccess.setAppId(message.getMessageHeader().getAppId());
            ctx.channel().writeAndFlush(loginSuccess);
        }else if(command==SystemCommand.LOGOUT.getCommand()){    //登出
            //用户退出
//            //删除session
//             String userId= (String) ctx.channel().attr(AttributeKey.valueOf(Constants.UserId)).get();
//             Integer appId= (Integer) ctx.channel().attr(AttributeKey.valueOf(Constants.AppId)).get();
//             Integer clientType= (Integer) ctx.channel().attr(AttributeKey.valueOf(Constants.ClientType)).get();
//             SessionSocketHolder.remove(appId,userId,clientType);
//             //删除redis
//            RedissonClient redissonClient = RedisManager.getRedissonClient();
//            RMap<Object, Object> map = redissonClient.getMap(appId + Constants.RedisConstants.UserSessionConstants + userId);
//            map.remove(clientType);
//            ctx.channel().close();   //进行关闭连接
            SessionSocketHolder.removeUserSession((NioSocketChannel) ctx.channel());    //进行用户退出 是前面代码的替代
        }else if(command==SystemCommand.PING.getCommand()){      //心跳检测
            ctx.channel().attr(AttributeKey.valueOf(Constants.ReadTime)).set(System.currentTimeMillis());
        } else if (command== MessageCommand.MSG_P2P.getCommand()|| command == GroupEventCommand.MSG_GROUP.getCommand()) {
            //如果是单聊或者是群聊的话进行校验
            try {
                String toId = "";
                CheckSendMessageReq req = new CheckSendMessageReq();
                req.setAppId(message.getMessageHeader().getAppId());
                req.setCommand(message.getMessageHeader().getCommand());
                JSONObject jsonObject = JSON.parseObject(JSONObject.toJSONString(message.getMessagePack()));
                String fromId = jsonObject.getString("fromId");
                if(command == MessageCommand.MSG_P2P.getCommand()){
                    toId = jsonObject.getString("toId");
                }else {
                    toId = jsonObject.getString("groupId");
                }
                req.setToId(toId);
                req.setFromId(fromId);

                ResponseVO responseVO = feignMessageService.checkSendMessage(req);
                if(responseVO.isOk()){
                    MqMessageProducer.sendMessage(message,command);
                }else{
                    Integer ackCommand = 0;
                    if(command == MessageCommand.MSG_P2P.getCommand()){
                        ackCommand = MessageCommand.MSG_ACK.getCommand();
                    }else {
                        ackCommand = GroupEventCommand.GROUP_MSG_ACK.getCommand();
                    }

                    ChatMessageAck chatMessageAck = new ChatMessageAck(jsonObject.getString("messageId"));
                    responseVO.setData(chatMessageAck);
                    MessagePack<ResponseVO> ack = new MessagePack<>();
                    ack.setData(responseVO);
                    ack.setCommand(ackCommand);
                    ctx.channel().writeAndFlush(ack);
                }
        }catch (Exception e){
                e.printStackTrace();
            }
//            CheckSendMessageReq req=new CheckSendMessageReq();
//            req.setAppId(message.getMessageHeader().getAppId());
//            req.setCommand(message.getMessageHeader().getCommand());
//            JSONObject jsonObject = JSON.parseObject(JSONObject.toJSONString(message.getMessagePack()));
//            String fromId = jsonObject.getString("fromId");
//            String toId = jsonObject.getString("toId");
//            req.setToId(toId);
//            req.setFromId(fromId);
//            ResponseVO responseVO = feignMessageService.checkSendMessage(req);
//            if (responseVO.isOk()){
//                MqMessageProducer.sendMessage(message,command);
//            }else {
//                //返回 ack
//                ChatMessageAck chatMessageAck = new ChatMessageAck(jsonObject.getString("messageId"));
//                responseVO.setData(chatMessageAck);
//                MessagePack<ResponseVO> ack = new MessagePack<>();
//                ack.setData(responseVO);
//                ack.setCommand(MessageCommand.MSG_P2P.getCommand());
//                ctx.channel().writeAndFlush(ack);
//            }
        }else {
            MqMessageProducer.sendMessage(message,command);
        }
    }


    //表示channel处于不活跃的状态
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //设置离线
        SessionSocketHolder.offlineUserSession((NioSocketChannel) ctx.channel());
        super.channelInactive(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        super.exceptionCaught(ctx, cause);
    }

}
